/// A type that represents a column in a database table.
///
/// ## Topics
///
/// ### Standard Columns
///
/// - ``rowID``
///
/// ### Deriving SQL Expressions
///
/// - ``detached``
/// - ``match(_:)-727nk``
/// - ``match(_:)-1vvo8``
///
/// ### Creating Column Assignments
///
/// - ``noOverwrite``
/// - ``set(to:)``
public protocol ColumnExpression: SQLSpecificExpressible {
    /// The column name.
    ///
    /// The column name is never qualified with a table name.
    ///
    /// For example, the name of a column can be `"score"`, but
    /// not `"player.score"`.
    var name: String { get }
}

extension ColumnExpression {
    /// Returns an SQL column.
    public var sqlExpression: SQLExpression {
        .column(name)
    }
    
    /// An SQL expression that refers to an aliased column
    /// (`expression AS alias`).
    ///
    /// Once detached, a column is never qualified with any table name in the
    /// SQL generated by the query interface.
    ///
    /// For example, see how `Column("total").detached` makes it possible to
    /// sort this query:
    ///
    /// ```swift
    /// struct Player: TableRecord {
    ///     static let team = belongsTo(Team.self)
    ///
    ///     enum Columns {
    ///         static let score = Column("score")
    ///         static let bonus = Column("bonus")
    ///     }
    /// }
    ///
    /// // SELECT player.*,
    /// //        (player.score + player.bonus) AS total,
    /// //        team.*
    /// // FROM player
    /// // JOIN team ON team.id = player.teamID
    /// // ORDER BY total, player.name
    /// //          ~~~~~
    /// let request = Player
    ///     .annotated { ($0.score + $0.bonus).forKey("total") }
    ///     .including(required: Player.team)
    ///     .order { [Column("total").detached, $0.name] }
    /// ```
    public var detached: SQLExpression {
        SQL(sql: name.quotedDatabaseIdentifier).sqlExpression
    }
}

extension ColumnExpression where Self == Column {
    /// The hidden rowID column.
    ///
    /// For example:
    ///
    /// ```swift
    /// // SELECT rowid FROM player
    /// let rowids = try Player.select(.rowID).fetchSet(db)
    /// ```
    public static var rowID: Self { Column.rowID }
}

/// A column in a database table.
///
/// For example:
///
/// ```swift
/// struct Player: FetchableRecord, TableRecord {
///     var score: Int
///
///     enum Columns {
///         static let score = Column("score")
///     }
/// }
///
/// try dbQueue.read { db in
///     // DELETE FROM player WHERE score = 0
///     try Player
///         .filter { $0.score == 0 }
///         .deleteAll(db)
///
///     // SELECT * FROM player ORDER BY score DESC LIMIT 10
///     let bestPlayers = try Player
///         .order(\.score)
///         .limit(10)
///         .fetchAll(db)
///
///     // SELECT MAX(score) FROM player
///     let maximumScore = try Player
///         .select({ max($0.score) }, as: Int.self)
///         .fetchOne(db)
/// }
/// ```
///
/// ## Topics
///
/// ### Standard Columns
///
/// - ``rowID-3bn70``
/// - ``rank``
///
/// ### Creating A Column
///
/// - ``init(_:)-5grmu``
/// - ``init(_:)-7xc4z``
public struct Column: Sendable {
    /// The hidden rowID column.
    public static let rowID = Column("rowid")
    
    public var name: String
    
    /// Creates a `Column` given its name.
    ///
    /// The name should be unqualified, such as `"score"`. Qualified name such
    /// as `"player.score"` are unsupported.
    public init(_ name: String) {
        self.name = name
    }
    
    /// Creates a `Column` given a `CodingKey`.
    public init(_ codingKey: some CodingKey) {
        self.name = codingKey.stringValue
    }
}

extension Column: ColumnExpression { }

/// Support for column enums:
///
/// ```swift
/// struct Player {
///     enum Columns: String, ColumnExpression {
///         case id, name, score
///     }
/// }
/// ```
extension ColumnExpression where Self: RawRepresentable, Self.RawValue == String {
    public var name: String { rawValue }
}
